package com.cb2.algorithm.leetcode;

/**
 * <a href='https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/'>买卖股票的最佳时机 II(Best Time to Buy and Sell Stock II)</a>
 * <p>给你一个整数数组 prices ，其中 prices[i] 表示某支股票第 i 天的价格。</p>
 * <p>在每一天，你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买，然后在 同一天 出售。</p>
 * <p>返回 你能获得的 最大 利润 。</p>
 *
 * <p>
 * <b>示例：</b>
 * <pre>
 *  示例 1：
 *      输入：prices = [7,1,5,3,6,4]
 *      输出：7
 *      解释：在第 2 天（股票价格 = 1）的时候买入，在第 3 天（股票价格 = 5）的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
 *          随后，在第 4 天（股票价格 = 3）的时候买入，在第 5 天（股票价格 = 6）的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
 *          最大总利润为 4 + 3 = 7 。
 *
 *  示例 2：
 *      输入：prices = [1,2,3,4,5]
 *      输出：4
 *      解释：在第 1 天（股票价格 = 1）的时候买入，在第 5 天 （股票价格 = 5）的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
 *          最大总利润为 4 。
 *
 *  示例 3：
 *      输入：prices = [7,6,4,3,1]
 *      输出：0
 *      解释：在这种情况下, 交易无法获得正利润，所以不参与交易可以获得最大利润，最大利润为 0。
 * </pre>
 * </p>
 *
 * <p>
 * <b>提示：</b>
 * <ul>
 *     <li>1 <= prices.length <= 3 * 10^4</li>
 *     <li>0 <= prices[i] <= 10^4</li>
 * </ul>
 * </p>
 *
 * @author c2b
 * @since 2025/2/17 14:02
 */
public class LC0122BestTimeToBuyAndSellStock_II_M {
    static class Solution {
        /**
         * 贪心算法。
         * <p>假设第0天买入，第3天卖出，那么利润为：nums[3] - nums[0]。</p>
         * <p>相当于：(nums[3]-nums[2]) + (nums[2]-nums[1]) + (nums[1]-nums[0])。即可以拆分成，前一天买入，当天卖出。</p>
         * <p>贪心：只收集每天正利润。</p>
         */
        public int maxProfit(int[] prices) {
            int res = 0;
            for (int i = 1; i < prices.length; i++) {
                // 如果第二天的价格高于前一天的，就买入并在第二天卖出
                if (prices[i] > prices[i - 1]) {
                    res += prices[i] - prices[i - 1];
                }
            }
            return res;
        }

        /**
         * 动态规划
         */
        public int maxProfit2(int[] prices) {
            // dp[i][0] 表示第i天交易完后手里没有股票的最大利润，
            // dp[i][1] 表示第i天交易完后手里持有一支股票的最大利润(i从0开始)。

            /*
            dp[i][0] 的转移方程，
                1.如果前一天交易完后手里没有股票，那么今天无法卖出股票，即 dp[i][0]=dp[i−1][0]，
                2.如果前一天交易完后手里持有股票(dp[i−1][1])，那么今天可以将其卖出获得 prices[i]的收益，即 dp[i][0]=dp[i-1][1]+prices[i] 。
            因此为了收益最大化，可列出如下的转移方程：dp[i][0]=max{dp[i−1][0],dp[i−1][1]+prices[i]}

            dp[i][1]，按照同样的方式考虑转移状态，
                1.如果前一天已经持有一支股票，那么今天无法再买入，即dp[i][1]=dp[i−1][1]，
                2.如果前一天没有买入股票(dp[i−1][0])，今天可以选择将其买入，并减少 prices[i] 的收益，即 dp[i][1]=dp[i-1][0]-prices[i] 。
            可以列出如下的转移方程：dp[i][1]=max{dp[i−1][1],dp[i−1][0]−prices[i]}
            */
            /*
                int n = prices.length;
                int[][] dp = new int[n][2];
                dp[0][0] = 0;
                dp[0][1] = -prices[0];
                for (int i = 1; i < n; i++) {
                    dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
                    dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
                }
                return dp[n - 1][0];
             */
            // 由于每一天的状态仅与前一天的状态有关，而与更早的状态都无关。
            // 因此没有必要存储无关的状态，只需要将 dp[i−1][0] 和 dp[i−1][1] 存放在两个变量中，通过它们计算出 dp[i][0] 和 dp[i][1] 并存回对应的变量。
            int n = prices.length;
            int dp0 = 0;
            int dp1 = -prices[0];
            for (int i = 1; i < n; i++) {
                int tempDp0 = Math.max(dp0, dp1 + prices[i]);
                int tempDp1 = Math.max(dp1, dp0 - prices[i]);
                dp0 = tempDp0;
                dp1 = tempDp1;
            }
            return dp0;
        }

        public static void main(String[] args) {
            Solution solution = new Solution();
            System.out.println(solution.maxProfit(new int[]{7, 1, 5, 3, 6, 4}));
            System.out.println(solution.maxProfit(new int[]{1, 2, 3, 4, 5}));
            System.out.println(solution.maxProfit(new int[]{7, 6, 4, 3, 1}));
        }
    }
}
